package com.demiroot.server;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.URL;
import java.util.Map;

import com.demiroot.server.HeaderData.HeaderField;
import com.demiroot.server.HeaderData.RequestMethod;
import com.demiroot.server.pages.Page;
import com.demiroot.server.pages.PhysicalPageDefault;



public class Request implements Runnable {
	Socket sock;
	Server server;
		
	public Request(Socket sock, Server server) throws Exception {
		this.sock = sock;
		this.server = server;
	}
	
	public void run() {
		OutputStream os = null;
		try {
			os = sock.getOutputStream();
			InputStream is = sock.getInputStream();
			String head = ServerUtils.getHeaderData(is);
			HeaderData header = new HeaderData(head);
			
			byte[] content = getContent(is, header.getInt(HeaderField.CONTENT_LENGTH));
			servePage(os, header, content, server);			
			Thread.sleep(300);
			sock.close();
		} catch (Exception e) {
			try {
				e.printStackTrace();
				ServerUtils.sendResponse(os, makeErrorResponse(e, server.getConfig()));
			} catch (Exception e2) {
				System.err.println("Error: Could not generate error page");
				e2.printStackTrace();
				System.err.println("Root Cause: ");
				e.printStackTrace();
			}
		}
	}
	
	private void servePage(OutputStream os, HeaderData header, byte[] content, Server server) throws Exception {
		HTTPResquest request = new HTTPRequestImpl(content,header,server,null);
		String session = header.getCookieValue("session");
		ServerSession ss = null;
		if (session != null) {
			ss = server.getSession(session);
			request.setSession(ss);
		}
		
		Map<String,String> vars = null;
		if (header.get(HeaderField.CONTENT_TYPE) != null 
				&&header.get(HeaderField.CONTENT_TYPE ).contains("x-www-form-urlencoded")) {
			vars = ServerUtils.seperateContent(content);
			Logger.log("url-encoded-vars",vars);
		} else if (header.getInt(HeaderField.CONTENT_LENGTH) != 0) {
			//do something
		}
		request.setVars(vars);
		
		if (header.getURI().startsWith("http://") && server.isProxyEnabled()) {
			try {
				ProxyIntercept pi = (ProxyIntercept)Class.forName(server.getConfig().getProxyIntercept()).newInstance();
				ProxyRequest pr = new ProxyRequest(os, request, pi);
				pr.run();
				return;
			} catch (Throwable e) {
				e.printStackTrace();
				throw new RuntimeException("Could not make proxy intersecpt");
			}
		}
		
		
		HTTPResponse page = getPage(header, content, server, request);
				
		if (header.getRequestMethod() == RequestMethod.GET 
				|| header.getRequestMethod() == RequestMethod.POST) {	
			ServerUtils.sendResponse(os, page);
		} else if (header.getRequestMethod() == RequestMethod.HEAD) {
			ServerUtils.sendResponse(os, page);
		}
	}

	private byte[] getContent(InputStream is, int length) throws IOException {
		byte[] ret = new byte[length];
		for (int x = 0; x < length; x++) {
			ret[x] = (byte)is.read();
		}
		return ret;
	}
	
	private HTTPResponse makeErrorResponse(Throwable t, Config config) {
		return new ErrorReportResponse(t, config);
	}
	
	private HTTPResponse getPage(HeaderData header, byte[] content, Server server, HTTPResquest request) 
		throws Exception {
		
		int authLevel = 0;
		if (request.getSession() != null) {
			String userID = (String)request.getSession().getVar("user");
			if (userID != null) {
				User u = server.getUserManager().getUserByUserID(userID);
				authLevel = u.getLevel();
			}
		}
		
		for (Class p : server.getJavaPages()) {
			Method isThisYou = server.getIsThisYouMethod(p);
			if (isThisYou != null) {
				Boolean b = (Boolean)isThisYou.invoke(p,header.getURI());
				if (b.equals(Boolean.TRUE)) {
					Page page = (Page)p.newInstance();
					return page.getPage(request);
				}
			}
		}
		
		URL fileURL = null;
		for (Config.Folder fileT : server.getConfig().getHtdocsLocs()) {
			if (fileT.getAuthLvl() <= authLevel) {
				fileURL = ClassLoader.getSystemResource(fileT.getLocation() + "/" + header.getURI());
				if (fileURL != null) break;
			} 
		}
		
		if (fileURL  == null || !(new File(fileURL.toURI()).exists()) 
				|| !(new File(fileURL.toURI()).canRead())) {
			Class p = null;
			try {
				p = Class.forName(server.getConfig().getNotFoundPage());
			} catch (Exception e) {
				throw new RuntimeException("Error, 404 page not found");
			}
			Page page = (Page) p.newInstance();
			return page.getPage(request);
		}
		
		Class p = null;
		try {
			if (server.getConfig().getPhysicalPage() == null) {
				p = PhysicalPageDefault.class;
			} else {
				p = Class.forName(server.getConfig().getPhysicalPage());
			}
		} catch (Exception e) {
			throw new RuntimeException("Error: Physical Page Responder");
		}
		Page page = (Page) p.newInstance();
		header.setURI(fileURL.toExternalForm());
		return page.getPage(request);
	}
}